package ssq.gamest.toolchain;

import java.io.File
import java.util.List
import ssq.gamest.toolchain.ReflectTreeBuilder.Inequality
import ssq.gamest.toolchain.ReflectTreeBuilder.PointBound
import ssq.gamest.toolchain.ReflectTreeBuilder.PointWithColor
import ssq.gamest.toolchain.ReflectTreeBuilder.PokerPattern
import ssq.gamest.toolchain.ReflectTreeBuilder.PokerPatternTerm
import ssq.utils.FileUtils
import java.util.LinkedList
import java.util.Vector

public class PokerPatternDetecterGenerator
{
	val GeneratorSettings settings;
	val ruleParser = new RuleParser()

	def static String matchTopTemplate(String gameName, String patternName, String body, String suffix, String leastCards)
	'''
package ssq.gamest.game.«gameName».patterns;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.Stack;

import ssq.utils.Pair;

import ssq.gamest.game.Poker;
import ssq.gamest.game.Color;
import ssq.gamest.game.PokerCardGroup;
import ssq.gamest.game.PokerOrderSettings;
import ssq.gamest.game.PokerPattern;

/**
* Subclass of PokerPattern. Auto-generated by Gamest-ToolChain.
*/
public class «patternName» extends PokerPattern
{
	public static final int leastCards = «leastCards»;
    /**
     * 所有的term并联, 前边的优先采用.
     */
    @Override
    public List<Pair<PokerPattern, PokerCardGroup>> match(PokerCardGroup pokerCardGroup, boolean partial, boolean first)
    {
        «body»
        return emptyResult;
    }
    
    «suffix»
}'''

	def static String matchSubTemplate(String suffix, String body)
	'''
public List<Pair<PokerPattern, PokerCardGroup>> match«suffix»(PokerCardGroup pokerCardGroup, boolean partial, boolean first)
{
    try
    {
        PokerOrderSettings settings = pokerCardGroup.game.settings;
        //记录匹配后剩余的牌
        Stack<PokerCardGroup> searchStack = new Stack<PokerCardGroup>();
        PokerCardGroup tmp = pokerCardGroup;
        List<Pair<PokerPattern, PokerCardGroup>> result = new LinkedList<Pair<PokerPattern, PokerCardGroup>>();

        «body»

        return result;
    }
    catch (Exception e)
    {
        return emptyResult;
    }
}'''

	def static String constructorTemplate(String patternName, List<String> assertEquals)
	{
		var sb = new StringBuilder
		var arguments = new StringBuilder

		for (String str : assertEquals)
		{
			var type = if(str.contains("Color"))
				{
					"Color "
				}
				else
				{
					"int "
				}

			if(assertEquals.contains(str))
				sb.append('''public «type»''').append(str).append(";\n")

			arguments.append(", ").append(type).append(str)
		}

		sb.append(
			'''
			public «patternName»(){}
			
			public «patternName»(Poker game«arguments», int sumOfCards, List<Integer> weights)
			{
				super(game, sumOfCards, weights);
				«FOR argu : assertEquals»
					this.«argu» = «argu»;
				«ENDFOR»
			}
			
			public boolean canCompare(PokerPattern o)
			{
                if (o.getClass() != getClass())
                {
                	return false;
                }
                else
                {
                    return «{var tmp = new Vector(assertEquals.map[it+" == (("+patternName+")o)."+it]) tmp.add("true") tmp.join(" && ")}»;
                }
			}
		''')

		sb.toString
	}

	new(GeneratorSettings s)
	{
		settings = s
	}

	def void run(String patternName, String description)
	{
		val root = ruleParser.getRoot(description) as PokerPattern
		var code = null as String

		if(root.ellipsed)
		{
			code = matchTopTemplate(settings.gameName, patternName, "// TO-DO", "// TO-DO", "// TO-DO")
		}
		else
		{
			var result = gen(patternName, root.pokerPatternExpression.assertEquals, root)
			code = matchTopTemplate(settings.gameName, patternName, result.get(0), result.get(1) + constructorTemplate(patternName, root.pokerPatternExpression.assertEquals), result.get(2))
		}

		val dir = new File(settings.genDir + settings.gameName + "/patterns")
		dir.mkdirs();

		FileUtils.saveString2File(new File(dir, patternName + ".java"), code);
	}

	/**
	 * 得到各egroupList的matchi函数, 并合并出一个match函数体
	 */
	def List<String> gen(String patternName, List<String> assertEquals, PokerPattern node)
	{
		var leastCards = Integer.MAX_VALUE
		if(node.ellipsed)
		{
			#["// TODO not implemented", ""];
		}
		else
		{
			var cnt = 0;
			var suffix = "";
			var body = "";

			for (PokerPatternTerm pokerPatternTerm : node.pokerPatternExpression)
			{
				body += '''
					List<Pair<PokerPattern, PokerCardGroup>> match«cnt» = match«cnt»(pokerCardGroup, partial, first);
					if (match«cnt».size() > 0)
					{
					    return match«cnt»;
					}
				'''
				var result = gen(patternName, assertEquals, pokerPatternTerm)

				suffix += matchSubTemplate(cnt.toString, result.get(0))
				leastCards = Math.min(leastCards, Integer.valueOf(result.get(1)))

				cnt++
			}

			#[body, suffix, leastCards.toString]
		}
	}

	val TAG = "<<Tag>>"

	def String[] gen(String patternName, List<String> assertEquals, PokerPatternTerm node)
	{
		var leastCards = 0

		for (PointWithColor p : node.pointsWithColor)
		{
			leastCards += (try
			{
				var o1 = p.lQuantifier.inequality.ordinal
				if(o1 < 2) 1 else if(o1 < 4) Integer.valueOf(p.lQuantifier.expression) else Integer.valueOf(p.lQuantifier.expression) + 1
			}
			catch(Exception e)
			{
				1
			})
			*
			(try
			{
				var o2 = p.rQuantifier.inequality.ordinal
				if(o2 < 2) 1 else if(o2 < 4) Integer.valueOf(p.rQuantifier.expression) else Integer.valueOf(p.rQuantifier.expression) + 1
			}
			catch(Exception e2)
			{
				1
			})
		}

		var depth = 0 // 给机器看的, 从0开始
		var body = TAG

		var fixedPoint = new LinkedList<Integer>
		var fixedColor = new LinkedList<Integer>

		for (PointWithColor pointWithColor : node.pointsWithColor)
		{
			var result = gen(pointWithColor, depth++, fixedPoint, fixedColor)
			body = body.replace(TAG, result)
		}

		body = body.replace(TAG, '''
			if (partial || (tmp.size() == 0))
			{
			    result.add(new Pair<PokerPattern, PokerCardGroup>(new «patternName»(game,«FOR e : assertEquals»«e», «ENDFOR» searchStack.peek().size() - tmp.size(), Arrays.asList(«if(node.weightExpressions!=null) node.weightExpressions.join(",")else 1»)), tmp));
			    
			    if (first)
			    {
			        return result;
			    }
			}
		''')

		#[body, leastCards.toString]
	}

	static val char ValBase = '0'
	static val char ColorBase = 'A'
	static val char PointBase = 'a'

	def String gen(PointWithColor node, int depth, LinkedList<Integer> fixedPoint, LinkedList<Integer> fixedColor)
	{
		var result = '''
		searchStack.push(tmp);
		«TAG»
		searchStack.pop();'''

		var needNext = false

		if(node.lQuantifier == null)
		{
			result = result.replace(TAG, '''final int l«depth.toString» = 1;
				«TAG»''')
		}
		else if(node.lQuantifier.inequality == null || node.lQuantifier.inequality == Inequality.E)
		{
			result = result.replace(TAG, '''final int l«depth» = «node.lQuantifier.expression»;
				«TAG»
				''')
		}
		else
		{
			val ll = node.lQuantifier.inequality.ordinal < 2
			needNext = true
			result = result.replace(TAG, '''
			for (int l«depth» = «if(ll) 1 else node.lQuantifier.expression + if(node.lQuantifier.inequality == Inequality.G) "+1" else ""»;next«depth» 
			«if(ll)
				''' && l«depth» «node.lQuantifier.inequality.toString» «node.lQuantifier.expression»'''
				else
					""»;l«depth»++)
			{
				«TAG»
			} ''')
		}

		if(node.rQuantifier == null)
		{
			result = result.replace(TAG, 'final int r' + depth + '= 1;' + TAG)
		}
		else if(node.rQuantifier.inequality == null || node.rQuantifier.inequality == Inequality.E)
		{
			result = result.replace(TAG, '''final int r«depth» = «node.rQuantifier.expression»;
				«TAG»''')
		}
		else
		{
			val rl = node.rQuantifier.inequality.ordinal < 2
			needNext = true
			result = result.replace(TAG, '''
			for (int r«depth» = «if(rl) 1 else node.rQuantifier.expression + if(node.rQuantifier.inequality == Inequality.G) "+1" else ""»;next«depth» «if(rl)
				''' && r«depth» «node.rQuantifier.inequality.toString» «node.rQuantifier.expression»'''
				else
					""»;r«depth»++)
			{
				«TAG»
			} ''')
		}

		if(needNext)
		{
			result = '''boolean next«depth»=true;
			''' + result.replace(TAG, '''
			next«depth» = false; 
			«TAG»''')
		}

		var colorExpression = null as String
		var pointExpression = null as String

		if(node.color != null)
		{
			if(node.color.isSome && !node.color.not)
			{
				colorExpression = '''settings.someColors.get('«(ColorBase + node.color.index)as char»' - 'A')'''
			}
			else if(node.color.isVal)
			{
				colorExpression = '''Color.get(«node.color.index»)'''
			}
			else // 变量或者not some
			{
				colorExpression = if(node.color.not)
				'''someColor«depth»'''
				else
				'''«(ColorBase + node.color.index)as char»'''

				if(node.color.not || ! fixedColor.contains(node.color.index))
				{
					fixedColor.add(node.color.index)

					result = result.replace(TAG, '''
						for (Color «colorExpression» : Color.values())
						{
							«if(node.color.not)
							'''
							if(«colorExpression» == settings.someColors.get('«(ColorBase + node.color.index)as char»'-'A')) 
								continue;
							'''»
							«TAG»
						}
					''')
				}
			}
		}
		else
		{
			colorExpression = "null"
		}

		var lowerBound = null as PointBound
		var higherBound = null as PointBound

		if(node.point.isAny)
		{
			pointExpression = "0"
		}
		else if(node.point.isSome && !node.point.not)
		{
			pointExpression = '''settings.somePoints.get('«(PointBase + node.color.index)as char»' - 'a')'''
		}
		else if(node.point.isVal)
		{
			pointExpression = node.point.index.toString
		}
		else
		{
			pointExpression = if(node.point.not)
			'''somePoint«depth»'''
			else
			'''«(PointBase + node.point.index)as char»'''

			if(node.point.not || ! fixedPoint.contains(node.point.index))
			{

				// 取第一个可以被接受的上下限
				if(node.point.pointBounds != null)
					for (ssq.gamest.toolchain.ReflectTreeBuilder.PointBound b : node.point.pointBounds)
					{
						if(higherBound == null && b.inequality.ordinal <= 2)
						{
							higherBound = b
						}

						if(lowerBound == null && b.inequality.ordinal >= 2)
						{
							lowerBound = b
						}
					}

				var hb = if(higherBound != null)
						if(higherBound.isVal)
							((ValBase + higherBound.index)as char).toString
						else if(higherBound.isVar)
							((PointBase + higherBound.index)as char).toString
						else
						'''settings.somePoints.get('«(PointBase + higherBound.index)as char»'-'a'))'''
					else
						""

				result = result.replace(TAG, '''
					Vector<Integer> toBeScanned«depth» = settings.getScanList(«node.byOrder.toString»);
					int highIndex«depth» = «IF higherBound == null» toBeScanned«depth».size()-1 «ELSE»toBeScanned«depth».lastIndexOf(«higherBound.index»)«IF (higherBound.inequality == Inequality.L)» -1 «ENDIF»«ENDIF»;
					
					for (int index«depth» = «IF lowerBound == null» 0 «ELSE»toBeScanned«depth».indexOf(«lowerBound.index»)«IF (lowerBound.inequality == Inequality.G)» +1 «ENDIF»«ENDIF»; index«depth» <= highIndex«depth» +1 -r«depth»; index«depth»++)
					{
						int «pointExpression» = toBeScanned«depth».get(index«depth»);
						«if(node.point.not)
						'''
							if(somePoint«depth» == settings.somePoints.get('«(PointBase + node.point.index)as char»'-'a')) 
								continue;
							'''»
						«TAG»
					}
				''')
			}
		}

		result = result.replace(
			TAG,
			'''
				tmp = searchStack.peek().contains(l«depth», «pointExpression», «colorExpression», «node.byOrder.toString», r«depth», «IF lowerBound != null»«lowerBound.index»«ELSE»-1«ENDIF», «IF higherBound != null»«higherBound.index»«ELSE»-1«ENDIF»);
				
				if (tmp != null)
				{
				    «if(needNext)
				'''next«depth» = true;'''»
				    
				    «if(node.lQuantifier != null && node.lQuantifier.catchId > 0)
				'''int _«node.lQuantifier.catchId» = l«depth»;'''»
				    «if(node.rQuantifier != null && node.rQuantifier.catchId > 0)
				'''int _«node.rQuantifier.catchId» = r«depth»;'''»
				    
				    «TAG»
				}
			'''
		)

		result
	}
}
